﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Threading;
using System.Reflection;
using System.Collections;
using King.Text;
using SysDateTime = System.DateTime;

using King.Extensions;
using King.Reflection;
using King.Collections;
using King.Loader;
using King.CSharp.Reflection;
using King.Xml.Reflection;
using King.Xml.Reflection.Emit;
using King.Xml.CSharp;
using King.Xml.CSharp.Reflection.Emit;
using King.Xml;
using System.Dynamic;
using System.Linq.Expressions;

namespace King.Xml.CSharp {

    public abstract class XsAttributeAttribute : Attribute {
        internal XsAttributeAttribute() { }
    }

    public enum XsNameFormat {
        ToLowerFirst = 0,
        PreserveName,
    }
    public sealed class XsSchemaAttribute : XsAttributeAttribute {
        private string m_xNamespace;

        public XsSchemaAttribute(
            string xNamespace) {

            m_xNamespace = xNamespace;
        }

        public string XNamespace {
            get { return m_xNamespace; }
        }
        public XsNameFormat NameFormat { get; set; }
        public XsForm ElementFormDefault { get; set; }
        public XsForm AttributeFormDefault { get; set; }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    public sealed class XsPrefixAttribute : XsAttributeAttribute {
        private string m_prefix;
        private string m_xNamespace;
        private Type m_type;

        public XsPrefixAttribute(string xNamespace) {
            m_xNamespace = xNamespace;
        }
        public XsPrefixAttribute(string xNamespace, string prefix) {
            m_prefix = prefix;
            m_xNamespace = xNamespace;
        }
        public XsPrefixAttribute(Type type, string prefix) {
            m_prefix = prefix;
            m_type = type;
        }
        public XsPrefixAttribute(Type type) {
            m_type = type;
        }

        public string Prefix {
            get { return m_prefix; }
        }
        public string XNamespace {
            get { return m_xNamespace; }
        }
        public Type Type {
            get { return m_type; }
        }
    }

    public class XsAnyTypeAttribute : XsAttributeAttribute {
        internal XsAnyTypeAttribute() { }
        
        public Type AliasFor { get; set; }
        public Type DeserializedType { get; set; }
    }
    public class XsSimpleTypeAttribute : XsAnyTypeAttribute {
        public XsSimpleTypeAttribute() { }
    }
    public class XsEnumTypeAttribute : XsSimpleTypeAttribute {
        public XsEnumTypeAttribute() { }
        public Type BaseType { get; set; }
    }
    public class XsDeserializedValueAttribute : XsAttributeAttribute {
        private object m_value;
        public XsDeserializedValueAttribute(object value) {
            m_value = value;
        }
        public object Value {
            get { return m_value; }
        }
    }
    public class XsUnionTypeAttribute : XsSimpleTypeAttribute {
        private readonly Type[] m_types;

        public XsUnionTypeAttribute(params Type[] types) {
            m_types = types;
        }

        public Type[] Types {
            get { return m_types; }
        }
    }
    public class XsListTypeAttribute : XsSimpleTypeAttribute {
        private readonly Type m_itemType;

        public XsListTypeAttribute(Type itemType) {
            m_itemType = itemType;
        }

        public Type ItemType {
            get { return m_itemType; }
        }
    }
    public sealed class XsSerializerAttribute : XsAttributeAttribute {
    }
    public sealed class XsDeserializerAttribute : XsAttributeAttribute {
    }
    public sealed class XsLiteralFormatterAttribute : XsAttributeAttribute {
    }

    public abstract class XsFacetAttribute : XsAttributeAttribute {
        private object m_value;
        internal XsFacetAttribute(object value) {
            m_value = value;
        }
        public object Value {
            get { return m_value; }
        }
    }

    public sealed class XsCardinalityAttribute : XsFacetAttribute {
        public XsCardinalityAttribute(long value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }
    public sealed class XsMaximumCardinalityAttribute : XsFacetAttribute {
        public XsMaximumCardinalityAttribute(long value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }
    public sealed class XsMinimumCardinalityAttribute : XsFacetAttribute {
        public XsMinimumCardinalityAttribute(long value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }

    public sealed class XsLengthAttribute : XsFacetAttribute {
        public XsLengthAttribute(long value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }
    public sealed class XsMinimumLengthAttribute : XsFacetAttribute {
        public XsMinimumLengthAttribute(long value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }
    public sealed class XsMaximumLengthAttribute : XsFacetAttribute {
        public XsMaximumLengthAttribute(long value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }

    public sealed class XsMaximumInclusiveAttribute : XsFacetAttribute {
        public XsMaximumInclusiveAttribute(long value)
            : base(value) {
        }
        public XsMaximumInclusiveAttribute(string value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }
    public sealed class XsMaximumExclusiveAttribute : XsFacetAttribute {
        public XsMaximumExclusiveAttribute(long value)
            : base(value) {
        }
        public XsMaximumExclusiveAttribute(string value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }
    public sealed class XsMinimumInclusiveAttribute : XsFacetAttribute {
        public XsMinimumInclusiveAttribute(long value)
            : base(value) {
        }
        public XsMinimumInclusiveAttribute(string value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }
    public sealed class XsMinimumExclusiveAttribute : XsFacetAttribute {
        public XsMinimumExclusiveAttribute(long value)
            : base(value) {
        }
        public XsMinimumExclusiveAttribute(string value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }

    public sealed class XsTotalDigitsAttribute : XsFacetAttribute {
        public XsTotalDigitsAttribute(long value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }
    public sealed class XsFractionDigitsAttribute : XsFacetAttribute {
        public XsFractionDigitsAttribute(long value)
            : base(value) {
        }
        public bool IsFixed { get; set; }
    }

    public sealed class XsPatternAttribute : XsFacetAttribute {
        public XsPatternAttribute(params string[] value)
            : base(value) {
        }

        public bool IsFixed { get; set; }
    }
    public sealed class XsWhitespaceAttribute : XsFacetAttribute {
        public XsWhitespaceAttribute(XsWhitespaceFacetType value)
            : base(value) {
        }

        public bool IsFixed { get; set; }
    }

    public sealed class XsEnumerationAttribute : XsFacetAttribute {
        public XsEnumerationAttribute(Type value)
            : base(value) {
        }
        public XsEnumerationAttribute(params object[] value)
            : base(value) {
        }
    }
    public sealed class XsExplicitTimeZoneAttribute : XsFacetAttribute {
        public XsExplicitTimeZoneAttribute(XsExplicitTimezoneFacetType value)
            : base(value) {
        }

        public bool IsFixed { get; set; }
    }
    //public sealed class XsAssertionsAttribute : XsFacetAttribute {
    //    public bool IsFixed { get; set; }
    //}

    [XsSchema(Namespace)]
    public static class Xs {

        public const string Namespace = "http://www.w3.org/2001/XMLSchema";
        public const string DefaultPrefix = "xs";
        public static readonly XPrefix DefaultXPrefix = (XPrefix)DefaultPrefix;
        public static readonly XNamespace XNamespace = (XNamespace)Namespace;

        [XsAnyType(AliasFor = typeof(object))]
        public class AnyType {
        }

        [XsSimpleType]
        public class AnySimpleType : AnyType {
            internal AnySimpleType() {
                throw new InvalidOperationException();
            }
        }

        [XsSimpleType]
        public class AnyAtomicType : AnySimpleType {
        }

        [XsSimpleType(DeserializedType = typeof(string), AliasFor = typeof(string))]
        public class String : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(bool), AliasFor = typeof(bool))]
        public class Boolean : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(decimal), AliasFor = typeof(decimal))]
        public class Decimal : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(float), AliasFor = typeof(float))]
        public class Float : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(double), AliasFor = typeof(double))]
        public class Double : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(byte[]))]
        public class Base64Binary : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(byte[]))]
        public class HexBinary : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(Uri), AliasFor = typeof(Uri))]
        public class AnyUri : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(XName), AliasFor = typeof(XName))]
        public class QName : AnyAtomicType {
        }

        [XsSimpleType]
        public abstract class Duration : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(XsDateTime), AliasFor = typeof(XsDateTime))]
        public abstract class DateTime : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(XsDateTime))]
        public abstract class Date : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(XsDateTime))]
        public abstract class Time : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(XsDateTime))]
        public abstract class GYear : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(XsDateTime))]
        public abstract class GMonth : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(XsDateTime))]
        public abstract class GDay : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(XsDateTime))]
        public abstract class GYearMonth : AnyAtomicType {
        }

        [XsSimpleType(DeserializedType = typeof(XsDateTime))]
        public abstract class GMonthDay : AnyAtomicType {
        }

        [XsSimpleType]
        public class Integer : Decimal {
        }

        [XsSimpleType(DeserializedType = typeof(long), AliasFor = typeof(long))]
        public class Long : Integer {
        }

        [XsSimpleType(DeserializedType = typeof(int), AliasFor = typeof(int))]
        public class Int : Long {
        }

        [XsSimpleType(DeserializedType = typeof(short), AliasFor = typeof(short))]
        public class Short : Int {
        }

        [XsSimpleType(DeserializedType = typeof(sbyte), AliasFor = typeof(sbyte))]
        public class Byte : Short {
        }

        [XsSimpleType]
        public class NonNegativeInteger : Integer {
        }

        [XsSimpleType]
        public class PositiveInteger : NonNegativeInteger {
        }

        [XsSimpleType(DeserializedType = typeof(ulong), AliasFor = typeof(ulong))]
        public class UnsignedLong : NonNegativeInteger {
        }

        [XsSimpleType(DeserializedType = typeof(uint), AliasFor = typeof(uint))]
        public class UnsignedInt : UnsignedLong {
        }

        [XsSimpleType(DeserializedType = typeof(ushort), AliasFor = typeof(ushort))]
        public class UnsignedShort : UnsignedInt {
        }

        [XsSimpleType(DeserializedType = typeof(byte), AliasFor = typeof(byte))]
        public class UnsignedByte : UnsignedShort {
        }

        [XsSimpleType]
        public class NonPositiveInteger : Integer {
        }

        [XsSimpleType]
        public class NegativeInteger : NonPositiveInteger {
        }

        [XsSimpleType]
        public abstract class DayTimeDuration : Duration {
        }

        [XsSimpleType]
        public abstract class YearMonthDuration : Duration {
        }

        [XsSimpleType(DeserializedType = typeof(XsDateTime))]
        [XsExplicitTimeZone(XsExplicitTimezoneFacetType.Required)]
        public abstract class DateTimeStamp : DateTime {
        }
    }

    [XsSchema(Namespace)]
    [XsPrefix(typeof(Xs), XsLoader.XsdDefaultPrefix)]
    public static class Kn {

        public const string Namespace = "http://www.king.com/2012/XMLSchema";
        public const string DefaultPrefix = "kn";
        public static readonly XPrefix DefaultXPrefix = (XPrefix)DefaultPrefix;
        public static readonly XNamespace XNamespace = (XNamespace)Namespace;

        private const string OneDayDateTime = "24:00:00.000";

        [XsSimpleType(DeserializedType = typeof(DateTimeOffset), AliasFor = typeof(DateTimeOffset))]
        [XsPattern(@"((\+|\-)00:00|Z)$")]
        public abstract class DateTimeStamp : Xs.DateTimeStamp {

            [XsLiteralFormatter]
            private static string DenormalizeLiteral(string literal) {
                return literal.Replace("+00:00", "Z");
            }

            [XsDeserializer]
            internal static DateTimeOffset Deserialize(XsDateTime value) {
                return (DateTimeOffset)value;
            }

            [XsSerializer]
            internal static XsDateTime Serialize(DateTimeOffset value) {
                return (XsDateTime)value;
            }
        }

        [XsSimpleType(DeserializedType = typeof(TimeSpan), AliasFor = typeof(TimeSpan))]
        [XsExplicitTimeZone(XsExplicitTimezoneFacetType.Prohibited)]
        [XsMaximumExclusive(OneDayDateTime)]
        public abstract class Time : Xs.Time {

            [XsDeserializer]
            private static TimeSpan Deserialize(XsDateTime value) {
                return (SysDateTime)value - default(SysDateTime);
            }

            [XsSerializer]
            private static XsDateTime Serialize(TimeSpan value) {

                // timeSpan cannot be converted to DateTime if is negative
                if (value < default(TimeSpan))
                    throw new XsSerializationException();

                // convert
                return SysDateTime.MinValue + value;
            }
        }

        [XsSimpleType(DeserializedType = typeof(SysDateTime), AliasFor = typeof(SysDateTime))]
        [XsExplicitTimeZone(XsExplicitTimezoneFacetType.Prohibited)]
        [XsPattern(@"T(2(3|2|1|0)|1|0)")]
        public abstract class DateTime : Xs.DateTime {

            [XsDeserializer]
            internal static SysDateTime Deserialize(XsDateTime value) {
                return (SysDateTime)value;
            }

            [XsSerializer]
            internal static XsDateTime Serialize(SysDateTime value) {
                return (XsDateTime)value;
            }
        }

        [XsSimpleType(DeserializedType = typeof(SysDateTime))]
        [XsExplicitTimeZone(XsExplicitTimezoneFacetType.Prohibited)]
        public abstract class Date : Xs.Date {

            [XsDeserializer]
            private static SysDateTime Deserialize(XsDateTime value) {
                return DateTime.Deserialize(value);
            }

            [XsSerializer]
            private static XsDateTime Serialize(SysDateTime value) {
                return DateTime.Serialize(value);
            }
        }

        [XsSimpleType(DeserializedType = typeof(SysDateTime))]
        [XsExplicitTimeZone(XsExplicitTimezoneFacetType.Prohibited)]
        public abstract class Year : Xs.GYear {

            [XsDeserializer]
            private static SysDateTime Deserialize(XsDateTime value) {
                return DateTime.Deserialize(value);
            }

            [XsSerializer]
            private static XsDateTime Serialize(SysDateTime value) {
                return DateTime.Serialize(value);
            }
        }

        [XsSimpleType(DeserializedType = typeof(SysDateTime))]
        [XsExplicitTimeZone(XsExplicitTimezoneFacetType.Prohibited)]
        public abstract class Month : Xs.GMonth {

            [XsDeserializer]
            private static SysDateTime Deserialize(XsDateTime value) {
                return DateTime.Deserialize(value);
            }

            [XsSerializer]
            private static XsDateTime Serialize(SysDateTime value) {
                return DateTime.Serialize(value);
            }
        }

        [XsSimpleType(DeserializedType = typeof(SysDateTime))]
        [XsExplicitTimeZone(XsExplicitTimezoneFacetType.Prohibited)]
        public abstract class Day : Xs.GDay {

            [XsDeserializer]
            private static SysDateTime Deserialize(XsDateTime value) {
                return DateTime.Deserialize(value);
            }

            [XsSerializer]
            private static XsDateTime Serialize(SysDateTime value) {
                return DateTime.Serialize(value);
            }
        }

        [XsSimpleType(DeserializedType = typeof(SysDateTime))]
        [XsExplicitTimeZone(XsExplicitTimezoneFacetType.Prohibited)]
        public abstract class YearMonth : Xs.GYearMonth {

            [XsDeserializer]
            private static SysDateTime Deserialize(XsDateTime value) {
                return DateTime.Deserialize(value);
            }

            [XsSerializer]
            private static XsDateTime Serialize(SysDateTime value) {
                return DateTime.Serialize(value);
            }
        }

        [XsSimpleType(DeserializedType = typeof(SysDateTime))]
        [XsExplicitTimeZone(XsExplicitTimezoneFacetType.Prohibited)]
        public abstract class MonthDay : Xs.GMonthDay {

            [XsDeserializer]
            private static SysDateTime Deserialize(XsDateTime value) {
                return DateTime.Deserialize(value);
            }

            [XsSerializer]
            private static XsDateTime Serialize(SysDateTime value) {
                return DateTime.Serialize(value);
            }
        }
    }
}
namespace King.Xml.CSharp.Reflection {
    public sealed class XsCSharpLoaderException : Exception {
        internal XsCSharpLoaderException(string message)
            : base(message) {
        }
    }

    public abstract class XsCSharpObject {
        private XsCSharpLoader m_loader;

        internal XsCSharpObject(XsCSharpLoader loader) {
            m_loader = loader;
        }

        public XsCSharpLoader Loader {
            get { return m_loader; }
        }
        public XsLoader XsLoader {
            get { return Loader.XsLoader; }
        }
        public CSharpLoader CSharpLoader {
            get { return Loader.CSharpLoader; }
        }
    }
    public sealed class XsCSharpLoader {

        private readonly CSharpLoader m_csharpLoader;
        private readonly XsLoader m_xsLoader;
        private readonly Loader<CSharpAssembly, XsCSharpAssembly> m_assemblyLoader;
        private readonly Loader<CSharpType, XsCSharpSchema> m_schemaLoader;

        public XsCSharpLoader(
            CSharpLoader csharpLoader = null,
            Func<XsCSharpSchemaFactory, XsCSharpSchema> schemaFactory = null,
            Func<XNamespace, CSharpType> getSchemaType = null) {

            if (schemaFactory == null)
                schemaFactory = o => o.CreateSchema();

            m_csharpLoader = csharpLoader ?? CSharpLoader.Create();
            m_xsLoader = new XsLoader(xsSchemaFactory => {
                var schema = GetSchema(xsSchemaFactory.XNamespace);
                if (schema == null)
                    return null;

                return schema.CreateXsSchema(xsSchemaFactory);
            });

            Func<XNamespace, CSharpType> getSchemaTypeWithDefaults = o => {
                if (o == Xs.Namespace)
                    return m_csharpLoader.GetType(typeof(Xs));

                if (o == Kn.Namespace)
                    return m_csharpLoader.GetType(typeof(Kn));

                if (getSchemaType != null)
                    return getSchemaType(o);

                return null;
            };

            m_assemblyLoader = new Loader<CSharpAssembly, XsCSharpAssembly>(
                keys: o => null,
                defaultResolver: o => new XsCSharpAssembly(this, o.Key),
                getLoader: o => o.Loader.m_assemblyLoader,
                getKeys: o => new object[] {
                    o.CSharpAssembly,
                },
                normalizeKey: o => {
                    if (o is CSharpAssembly)
                        return (CSharpAssembly)o;

                    return null;
                }
            );

            m_schemaLoader = new Loader<CSharpType, XsCSharpSchema>(
                keys: o => null,
                getLoader: o => o.Loader.m_schemaLoader,
                getKeys: o => new object[] {
                    o.XNamespace,
                    o.CSharpType,
                },
                defaultResolver: o => schemaFactory(new XsCSharpSchemaFactory(this, o.Key)),
                normalizeKey: o => {
                    
                    var csharpType = o as CSharpType;
                    if (csharpType == null) {

                        var xNamespace = o as XNamespace;
                        if (xNamespace == null)
                            return null;

                        csharpType = getSchemaTypeWithDefaults(xNamespace);
                        if (csharpType == null)
                            return null;
                    }

                    if (!csharpType.IsDefined(typeof(XsSchemaAttribute)))
                        return null;

                    return csharpType;
                }
            );
        }

        public XsLoader XsLoader {
            get { return m_xsLoader; }
        }
        public CSharpLoader CSharpLoader {
            get { return m_csharpLoader; }
        }

        public XsCSharpAssembly GetAssembly(CSharpAssembly csharpAssembly) {
            return m_assemblyLoader.TryGet(csharpAssembly);
        }

        public XsCSharpSchema GetSchema(XNamespace xNamespace) {
            return m_schemaLoader.TryGet(xNamespace);
        }
        public XsCSharpSchema GetSchema(CSharpType csharpType) {
            return m_schemaLoader.TryGet(csharpType);
        }
        public XsCSharpSchema GetSchema(Type type) {
            return GetSchema(CSharpLoader.TryGetType(type));
        }
        public XsCSharpSchema GetSchema(XsSchema xsSchema) {
            return m_schemaLoader.TryGet(xsSchema.XNamespace);
        }

        public XsCSharpType GetType(CSharpType csharpType) {
            if (csharpType == null)
                return null;

            var schema = GetSchema(csharpType.DeclaringType);
            if (schema == null)
                return null;

            return schema.GetType(csharpType);
        }
        public XsCSharpType GetType(Type type) {
            return GetType(CSharpLoader.TryGetType(type.AssemblyQualifiedName));
        }
        public XsCSharpType GetType(string assemblyQualifiedName) {
            return GetType(CSharpLoader.GetType(assemblyQualifiedName));
        }
        public XsCSharpType GetType(XName xName) {
            return GetType(XsLoader.GetType(xName));
        }
        public XsCSharpType GetType(XsType xsType) {
            if (xsType == null)
                return null;

            var schema = GetSchema(xsType.Schema);
            if (schema == null)
                return null;

            return schema.GetType(xsType);
        }
    }
    public sealed class XsCSharpAssembly : XsCSharpObject {
        private readonly XsCSharpLoader m_csharpLoader;
        private readonly CSharpAssembly m_csharpAssembly;

        internal XsCSharpAssembly(
            XsCSharpLoader loader,
            CSharpAssembly csharpAssembly)
            : base(loader) {
            m_csharpLoader = loader;
            m_csharpAssembly = csharpAssembly;
        }

        private XsCSharpType GetType(XsCSharpType type) {
            if (type == null || type.Assembly != this)
                return null;
            return type;
        }
        private XsCSharpSchema GetSchema(XsCSharpSchema schema) {
            if (schema == null || schema.Assembly != this)
                return null;
            return schema;
        }

        public CSharpAssembly CSharpAssembly {
            get { return m_csharpAssembly; }
        }

        public IEnumerable<XsCSharpSchema> Schemas() {
            return m_csharpAssembly.Types()
                .Select(o => o.GetAttribute(typeof(XsSchemaAttribute)))
                .WhereNotDefault()
                .Select(o => GetSchema((XNamespace)o.XNamespace));
        }
        public XsCSharpSchema GetSchema(CSharpType csharpType) {
            return GetSchema(Loader.GetSchema(csharpType));
        }
        public XsCSharpSchema GetSchema(XNamespace xNamespace) {
            return GetSchema(Loader.GetSchema(xNamespace));
        }
        public XsCSharpSchema GetSchema(XsSchema xsSchema) {
            return GetSchema(Loader.GetSchema(xsSchema));
        }

        public IEnumerable<XsCSharpType> Types() {
            return Schemas().SelectMany(o => o.Types());
        }
        public XsCSharpType GetType(XName xName) {
            return GetType(Loader.GetType(xName));
        }
        public XsCSharpType GetType(CSharpType csharpType) {
            return GetType(Loader.GetType(csharpType));
        }
        public XsCSharpType GetType(Type type) {
            return GetType(Loader.GetType(type.AssemblyQualifiedName));
        }
        public XsCSharpType GetType(string fullName) {
            return GetType(Loader.GetType(m_csharpAssembly.GetType(fullName)));
        }
        public XsCSharpType GetType(XsType xsType) {
            return GetType(xsType.XName);
        }

        public override string ToString() {
            return CSharpAssembly.ToString();
        }
    }
    public sealed class XsCSharpSchema : XsCSharpObject {
        public static string ToCSharpName(XsNameFormat nameFormat, string localName) {
            if (string.IsNullOrEmpty(localName))
                return null;

            if (nameFormat == XsNameFormat.ToLowerFirst) {

                if (!char.IsLower(localName[0]))
                    throw new InvalidOperationException();

                localName = localName.ToUpperFirst();
            }

            return localName;
        }
        public static string ToXLocalName(XsNameFormat nameFormat, string name) {
            if (string.IsNullOrEmpty(name))
                return null;

            if (nameFormat == XsNameFormat.ToLowerFirst) {

                if (char.IsLower(name[0]))
                    throw new InvalidOperationException();

                name = name.ToLowerFirst();
            }

            return name;
        }

        private readonly XsCSharpLoader m_loader;
        private readonly Lazy<XsCSharpAssembly> m_assembly;
        private readonly CSharpType m_csharpType;
        private readonly XNamespace m_xNamespace;
        private readonly XsNameFormat m_nameFormat;
        private readonly Loader<CSharpType, XsCSharpType> m_typeLoader;
        private readonly Lazy<Dictionary<XPrefix, XNamespace>> m_xNamespaceByXPrefix;
        private readonly XsForm m_elementFormDefault;
        private readonly XsForm m_attributeFormDefault;
        private readonly Func<XsSchemaFactory, XsSchema> m_createXsSchema;
        private readonly Loader<string, XsCSharpElement> m_rootElementLoader;
        private readonly Loader<XName, XsCSharpAttributeGroup> m_attributeGroupLoader;
        private readonly Dictionary<CSharpType, CSharpType> m_aliases;

        internal XsCSharpSchema(
            XsCSharpLoader loader,
            CSharpType schemaType,
            Func<XsCSharpSchema, IEnumerable<object>> complexTypeNames,
            Func<XsCSharpTypeFactory, XsCSharpType> complexTypeFactory,
            Func<XsCSharpSchema, IEnumerable<object>> attributeGroupNames,
            Func<XsCSharpAttributeGroupFactory, XsCSharpAttributeGroup> attributeGroupFactory,
            Func<XsCSharpSchema, IEnumerable<object>> rootElementNames,
            Func<XsCSharpRootElementFactory, XsCSharpElement> rootElementFactory)
            : base(loader) {

            m_loader = loader;
            m_assembly = new Lazy<XsCSharpAssembly>(() => loader.GetAssembly(schemaType.Assembly));
            m_csharpType = schemaType;

            var schemaAttribute = m_csharpType.GetAttribute(typeof(XsSchemaAttribute));
            m_xNamespace = schemaAttribute.XNamespace;
            m_nameFormat = schemaAttribute.NameFormat;

            m_elementFormDefault = schemaAttribute.ElementFormDefault;
            if (m_elementFormDefault == XsForm.Undefined)
                m_elementFormDefault = XsForm.Unqualified;

            m_attributeFormDefault = schemaAttribute.AttributeFormDefault;
            if (m_attributeFormDefault == XsForm.Undefined)
                m_attributeFormDefault = XsForm.Unqualified;

            // prefix <-> namespace
            m_xNamespaceByXPrefix = new Lazy<Dictionary<XPrefix, XNamespace>>(() => {
                var prefixAttributes = m_csharpType.Attributes(typeof(XsPrefixAttribute));

                // prefix -> namespace
                var result = prefixAttributes.ToDictionary(

                    // explict prefix
                    o => (XPrefix)(string)o.Prefix ??

                        // default namespace
                        XPrefix.None,

                    // explict namespace
                    o => (XNamespace)(string)o.XNamespace ??

                        // implicit namespace
                        Loader.GetSchema((CSharpType)o.Type).XNamespace
                );

                return result;
            });

            var simpleTypes = m_csharpType.NestedTypes()
                .Where(o => o.IsDefined(typeof(XsAnyTypeAttribute)));

            m_aliases = (
                from t in simpleTypes
                let a = t.GetAttribute(typeof(XsAnyTypeAttribute))
                let alias = (CSharpType)a.AliasFor where alias != null
                select new { Key = alias, Value = t }
            ).ToDictionary(o => o.Key, o => o.Value);
            
            m_rootElementLoader = new Loader<string, XsCSharpElement>(
                keys: o => rootElementNames(this),
                defaultResolver: o => 
                    rootElementFactory(new XsCSharpRootElementFactory(this, o.Key)),
                getLoader: o => o.Schema.m_rootElementLoader,
                getKeys: o => new object[] {
                    o.Name
                },
                normalizeKey: o => o as string
            );

            m_attributeGroupLoader = new Loader<XName, XsCSharpAttributeGroup>(
                keys: o => rootElementNames(this),
                defaultResolver: o => 
                    attributeGroupFactory(new XsCSharpAttributeGroupFactory(this, o.Key)),
                getLoader: o => o.Schema.m_attributeGroupLoader,
                getKeys: o => new object[] {
                    o.Name,
                    o.XName
                },
                normalizeKey: o => o as string
            );

            m_typeLoader = new Loader<CSharpType, XsCSharpType>(
                keys: o => simpleTypes.Concat(complexTypeNames(this)).ToArray(),

                defaultResolver: o => {
                    var type = o.Key;

                    // special case (anyType; the only complexType)
                    if (CSharpType == typeof(Xs)) {

                        // resolve aliases
                        if (type == typeof(object))
                            type = CSharpLoader.GetType(typeof(Xs.AnyType));

                        // complexType (built-in)
                        if (type == typeof(Xs.AnyType)) {
                            return new XsCSharpTypeFactory(this, o.Key)
                                .CreateComplexTypeWithComplexContent(
                                    baseType: null,
                                    alias: CSharpLoader.GetType(typeof(object)),
                                    isAbstract: true
                                );
                        }
                    }

                    // simpleType (udt)
                    var simpleType = ResolveSimpleType(type);
                    if (simpleType != null)
                        return XsCSharpSimpleType.Activate(this, simpleType);

                    // complexType (udt)
                    return complexTypeFactory(new XsCSharpTypeFactory(this, type));
                },
                getLoader: o => o.Schema.m_typeLoader,
                getKeys: o => new object[] {
                    o.XName,
                    o.Name,
                    o.Alias,
                    o.CSharpType,
                },
                normalizeKey: o =>
                    XsCSharpResolver.ResolveNestedType(o, schemaType, XNamespace, ToCSharpName)
            );

            m_createXsSchema = xsSchemaFactory => xsSchemaFactory.CreateSchema(
                xNamespaceByXPrefix: m_xNamespaceByXPrefix,

                typeNames: o => Types().Select(x => x.XName),
                typeFactory: o => {
                    var type = GetType(o.XName);
                    if (type == null)
                        return null;

                    return type.CreateXsType(o);
                },

                attributeGroupNames: o => attributeGroupNames(this),
                attributeGroupFactory: null,

                rootElementNames: o => rootElementNames(this),
                rootElementFactory: o => {
                    var rootElement = GetRootElement(o.Name);
                    if (rootElement == null)
                        return null;

                    return o.CreateRootElement(rootElement.Type.XsType);
                },

                attributeDefaultForm: AttributeFormDefault,
                elementDefaultForm: ElementFormDefault,

                metadata: null
            );
        }
        private CSharpType ResolveSimpleType(CSharpType type) {
            ASSERT.Condition(type != null);
            if (type == null)
                return null;

            // allow SimpleContent type to be loaded elsewhere; e.g. derivation by SimpleContent<>
            var allSuperClassesXsTypes = type.ToLinkList(o => o.BaseType)
                .First(o => !o.IsDefined(typeof(XsAnyTypeAttribute))).Assembly == 
                typeof(object).Assembly;
            
             if (!allSuperClassesXsTypes)
                return null;
            
            // resolve alias
            if (!type.IsDefined(typeof(XsSimpleTypeAttribute)))
                type = m_aliases.GetValueOrDefault(type);

            if (type == null)
                return null;

            // verify DeclaringType
            if (type.DeclaringType != CSharpType)
                return null;

            return type;
        }

        internal XsSchema CreateXsSchema(XsSchemaFactory xsSchemaFactory) {
            return m_createXsSchema(xsSchemaFactory);
        }

        public CSharpAssembly CSharpAssembly {
            get { return CSharpType.Assembly; }
        }
        public CSharpType CSharpType {
            get { return m_csharpType; }
        }
        public XsCSharpAssembly Assembly {
            get { return m_assembly.Value; }
        }
        public XsSchema XsSchema {
            get { return XsLoader.GetSchema(XNamespace); }
        }
        public XNamespace XNamespace {
            get { return m_xNamespace; }
        }

        public XsNameFormat NameFormat {
            get { return m_nameFormat; }
        }
        public string ToCSharpName(string localName) {
            return ToCSharpName(NameFormat, localName);
        }
        public string ToXLocalName(string name) {
            return ToXLocalName(NameFormat, name);
        }
        public XName ToXName(string name, XsForm xsForm = XsForm.Qualified) {

            if (xsForm == XsForm.Undefined)
                xsForm = XsForm.Qualified;

            var xNamespace = XNamespace;
            if (xsForm == XsForm.Unqualified)
                xNamespace = XNamespace.None;

            return xNamespace + ToXLocalName(name);
        }

        public XsForm AttributeFormDefault {
            get { return m_attributeFormDefault; }
        }
        public XsForm ElementFormDefault {
            get { return m_elementFormDefault; }
        }

        public IEnumerable<XPrefix> Prefixes() {
            return XsSchema.Prefixes();
        }
        public IEnumerable<XPrefix> Prefixes(XsSchema xsSchema) {
            return XsSchema.Prefixes(xsSchema);
        }
        public IEnumerable<XPrefix> Prefixes(XsCSharpSchema schema) {
            return Prefixes(schema.XsSchema);
        }
        public IEnumerable<XPrefix> Prefixes(XNamespace xNamespace) {
            return XsSchema.Prefixes(xNamespace);
        }

        public XQName GetQName(XName xName) {
            return XsSchema.GetQName(xName);
        }
        public XQName GetQName(XsCSharpType type) {
            if (type == null)
                return null;
            return XsSchema.GetQName(type.XsType);
        }
        public XQName GetQName(Type type) {
            return GetQName(Loader.GetType(type));
        }
        public XQName GetQName(CSharpType type) {
            return GetQName(Loader.GetType(type));
        }
        public XsCSharpSchema GetSchema(XPrefix prefix) {
            return Loader.GetSchema(XsSchema.GetSchema(prefix));
        }
        public XNamespace GetXNamespace(XPrefix prefix) {
            return XsSchema.GetXNamespace(prefix);
        }
        public XName GetXName(XQName qName) {
            return XsSchema.GetXName(qName);
        }

        public IEnumerable<KeyValuePair<CSharpType, CSharpType>> Aliases() {
            return m_aliases;
        }
        public CSharpType GetAlias(CSharpType type) {
            return m_aliases.GetValueOrDefault(type);
        }

        public IEnumerable<XsCSharpType> Types() {
            return m_typeLoader.Values();
        }
        public XsCSharpType GetType(XName xName) {
            return m_typeLoader.TryGet(xName);
        }
        public XsCSharpType GetType(string name) {
            return m_typeLoader.TryGet(name);
        }
        public XsCSharpType GetType(CSharpType csharpType) {
            return m_typeLoader.TryGet(csharpType);
        }
        public XsCSharpType GetType(XsType xsType) {
            return GetType(xsType.XName);
        }
        public XsCSharpType GetType(Type type) {
            return GetType(CSharpLoader.TryGetType(type));
        }
        public XsCSharpType GetType(XQName qName) {
            return Loader.GetType(XsSchema.GetType(qName));
        }

        public IEnumerable<XsCSharpAttributeGroup> AttributeGroups() {
            return m_attributeGroupLoader.Values();
        }
        public XsCSharpAttributeGroup GetAttributeGroup(XName name) {
            return m_attributeGroupLoader.TryGet(name);
        }
        public XsCSharpAttributeGroup GetAttributeGroup(string name) {
            return m_attributeGroupLoader.TryGet(name);
        }

        public IEnumerable<XsCSharpElement> RootElements() {
            return m_rootElementLoader.Values();
        }
        public XsCSharpElement GetRootElement(string name) {
            return m_rootElementLoader.TryGet(name);
        }
        public XsCSharpElement GetRootElement(XName name) {
            if (name.Namespace != XNamespace)
                return null;
            return GetRootElement(name.LocalName);
        }

        public XsCSharpComplexContent GetContent(XsComplexContent xsContent) {
            throw new NotImplementedException();
        }

        public override string ToString() {
            return CSharpType.ToString();
        }
    }

    public abstract class XsCSharpType : XsCSharpObject {
        private readonly XsCSharpSchema m_schema;
        private readonly CSharpType m_csharpType;

        internal XsCSharpType(
            XsCSharpSchema schema,
            CSharpType csharpType) 
            : base(schema.Loader) {

            m_schema = schema;
            m_csharpType = csharpType;
        }

        internal abstract XsType CreateXsType(XsTypeFactory typeFactory);

        internal bool IsXsdType {
            get { return XsSchema == XsLoader.Xsd; }
        }

        #region Identity
        public XsSchema XsSchema {
            get { return XsLoader.GetSchema(XNamespace); }
        }
        public XNamespace XNamespace {
            get { return Schema.XNamespace; }
        }
        public CSharpAssembly CSharpAssembly {
            get { return Assembly.CSharpAssembly; }
        }
        public XsCSharpAssembly Assembly {
            get { return Schema.Assembly; }
        }
        public XsCSharpSchema Schema {
            get { return m_schema; }
        }

        public abstract CSharpType Alias { get; }
        public abstract XsType XsType { get; }

        public CSharpType CSharpType {
            get { return m_csharpType; }
        }
        public abstract string Name { get; }
        public abstract XName XName { get; }

        public virtual IEnumerable<XsCSharpType> UnionTypes(bool declaredOnly = false) {
            return Enumerable.Empty<XsCSharpType>();
        }
        public virtual XsCSharpType ListType {
            get { return null; }
        }
        #endregion

        #region Type
        public bool IsUr {
            get { return XsType.IsUr; }
        }

        public bool IsSimpleType {
            get { return this is XsCSharpSimpleType; }
        }
        public bool IsAtomicType {
            get { return XsType.IsAtomicType; }
        }
        public bool IsList {
            get { return XsType.IsList; }
        }
        public bool IsUnion {
            get { return XsType.IsUnion; }
        }

        public bool IsComplexType {
            get { return XsType.IsComplexType; }
        }
        public bool IsGroup {
            get { return XsType.IsGroup; }
        }

        public bool IsSubclassOf(Type type) {
            return IsSubclassOf(Loader.GetType(type));
        }
        public bool IsSubclassOf(CSharpType type) {
            return IsSubclassOf(Loader.GetType(type));
        }
        public bool IsSubclassOf(XsType xsType) {
            return BaseType.IsOrIsSubclassOf(xsType);
        }
        public bool IsSubclassOf(XsCSharpType type) {
            return BaseType.IsOrIsSubclassOf(type.XsType);
        }

        public bool IsOrIsSubclassOf(Type type) {
            return IsOrIsSubclassOf(Loader.GetType(type));
        }
        public bool IsOrIsSubclassOf(CSharpType type) {
            return IsOrIsSubclassOf(Loader.GetType(type));
        }
        public bool IsOrIsSubclassOf(XsType xsType) {
            if (xsType == null)
                return false;
            return XsType.IsOrIsSubclassOf(xsType);
        }
        public bool IsOrIsSubclassOf(XsCSharpType type) {
            if (type == null)
                return false;
            return IsOrIsSubclassOf(type.XsType);
        }
        #endregion

        #region Derivation
        public abstract XsCSharpType BaseType {
            get;
        }
        public XsType BaseXsType {
            get {
                if (BaseType == null)
                    return null;
                return BaseType.XsType; 
            }
        }

        public bool IsAbstract {
            get { return XsType.IsAbstract; }
        }
        public bool IsExtension {
            get { return XsType.IsExtension; }
        }
        public bool IsRestriction {
            get { return XsType.IsRestriction; }
        }

        public bool IsFinalRestriction {
            get { return XsType.IsFinalRestriction; }
        }
        public bool IsFinalExtension {
            get { return XsType.IsFinalExtension; }
        }
        public bool IsFinalList {
            get { return XsType.IsFinalList; }
        }
        public bool IsFinalUnion {
            get { return XsType.IsFinalUnion; }
        }
        public bool IsMixed {
            get { return XsType.IsMixed; }
        }
        #endregion

        #region Facets
        public XsCardinality Cardinality {
            get { return XsType.Cardinality; }
        }
        public XsOrdered Orderd {
            get { return XsType.Orderd; }
        }
        public bool IsBounded {
            get { return XsType.IsBounded; }
        }
        public bool IsNumeric {
            get { return XsType.IsNumeric; }
        }

        public IEnumerable<XsFacet> Facets(XsFacetType? type = null, bool declaredOnly = false) {
            return XsType.Facets(type, declaredOnly);
        }
        public XsFacet GetFacet(XsFacetType facetType) {
            return XsType.GetFacet(facetType);
        }
        public dynamic TryGetFacetValue(XsFacetType facetType) {
            return XsType.Facets(facetType);
        }

        public XsFacetType ApplicableFacets {
            get { return XsType.ApplicableFacets; }
        }
        public XsFacetType FixedFacets {
            get { return XsType.FixedFacets; }
        }
        public XsFacetType AppliedFacets {
            get { return XsType.AppliedFacets; }
        }
        public XsFacetType OpenFacets {
            get { return XsType.OpenFacets; }
        }
        #endregion

        #region Serialization
        public virtual CSharpType DeserializedType {
            get { return null; }
        }
        public virtual dynamic Deserialize(string value) {
            throw new InvalidOperationException();
        }
        public virtual string Serialize(object value) {
            throw new InvalidOperationException();
        }

        public virtual bool IsStreamable {
            get { return XsType.IsStreamable; }
        }
        public virtual TextReader Deserialize(TextReader stream) {
            return XsType.Deserialize(stream);
        }
        #endregion

        #region ComplexType
        public virtual bool HasSimpleContent {
            get { return false; }
        }
        public virtual bool HasComplexContent {
            get { return false; }
        }
        public virtual XsCSharpType SimpleContent {
            get { return null; }
        }
        public virtual XsCSharpComplexContent ComplexContent {
            get { return null; }
        }
        public virtual IEnumerable<XsCSharpAttribute> Attributes(bool declaredOnly = false) {
            throw new InvalidOperationException();
        }
        public virtual IEnumerable<XsCSharpAttributeGroup> AttributeGroups() {
            throw new InvalidOperationException();
        }
        #endregion

        public override string ToString() {
            return CSharpType.ToString();
        }
    }
    internal sealed class XsCSharpComplexType : XsCSharpType {
        private readonly CSharpType m_csharpTypeAlias;
        private readonly Lazy<XsCSharpType> m_simpleContent;
        private readonly Lazy<XsCSharpComplexContent> m_complexContent;
        private readonly Func<XsCSharpComplexType, XsTypeFactory, XsType> m_getXsType;
        private readonly XsCSharpType m_baseType;

        internal XsCSharpComplexType(
            XsCSharpSchema schema, 
            XsCSharpType baseType,
            CSharpType csharpType,
            CSharpType csharpTypeAlias,
            Func<XsCSharpSimpleContentFactory, XsCSharpType> getSimpleContent,
            Func<XsCSharpComplexType, XsTypeFactory, XsType> getXsType
        ) : base(schema, csharpType) {

            m_baseType = baseType;
            m_getXsType = getXsType;
            m_csharpTypeAlias = csharpTypeAlias;

            m_simpleContent = new Lazy<XsCSharpType>(() => {
                if (getSimpleContent == null)
                    return null;

                var simpleContent = getSimpleContent(
                    new XsCSharpSimpleContentFactory(this)
                );

                // must be simple
                ASSERT.Condition(simpleContent.IsSimpleType);

                // simpleContent's baseType must be the base complexType's simpleContent
                ASSERT.Condition(BaseType.SimpleContent == null ||
                    BaseType.SimpleContent == simpleContent.BaseType);

                // first simpleContent must be found on first UDT complexType in hierarcy
                ASSERT.Condition(BaseType == Loader.GetType(typeof(Xs.AnyType)) ||
                    BaseType.SimpleContent != null);

                return simpleContent;
            });

            // simpleContent must be inherited
            ASSERT.Condition(
                BaseType == null ||
                BaseType.SimpleContent == null ||
                getSimpleContent != null
            );

            m_complexContent = new Lazy<XsCSharpComplexContent>(() => null);
        }

        internal override XsType CreateXsType(XsTypeFactory typeFactory) {
            return m_getXsType(this, typeFactory);
        }
        internal XsType CreateXsSimpleContent(XsTypeFactory typeFactory) {

            // anonymous 
            if (SimpleContent.XName == null)
                return SimpleContent.CreateXsType(typeFactory);

            // named
            return SimpleContent.XsType;
        }

        public override XsCSharpType BaseType {
            get { return m_baseType; }
        }
        public override XsType XsType {
            get { return XsSchema.GetType(XName); }
        }
        public override CSharpType Alias {
            get { return m_csharpTypeAlias; }
        }

        public override string Name {
            get { return CSharpType.Name; }
        }
        public override XName XName {
            get { return XNamespace + Schema.ToXLocalName(Name); }
        }

        public override bool HasSimpleContent {
            get { return SimpleContent != null; }
        }
        public override bool HasComplexContent {
            get { return ComplexContent != null; }
        }
        public override XsCSharpType SimpleContent {
            get { return m_simpleContent.Value; }
        }
        public override XsCSharpComplexContent ComplexContent {
            get { return m_complexContent.Value; }
        }
        public override IEnumerable<XsCSharpAttribute> Attributes(bool declaredOnly = false) {
            throw new NotImplementedException();
            //return XsType.Attributes(declaredOnly).Select(o => GetAttribute(o));
        }
        public override IEnumerable<XsCSharpAttributeGroup> AttributeGroups() {
            throw new NotImplementedException();
            //return XsType.AttributeGroups().Select(o => GetAttributeGroup(o));
        }
    }

    internal abstract class XsCSharpSimpleType : XsCSharpType {

        private const string SerializeMethodName = "Serialize";
        private const string DeserializeMethodName = "Deserialize";
        private const string DenormalizeLiteralMethodName = "DenormalizeLiteral";

        internal static XsCSharpSimpleType Activate(
            XsCSharpSchema schema,
            CSharpType csharpType,
            XsCSharpType baseType = null,
            XsCSharpType declaringType = null) {

            ASSERT.Condition(csharpType.IsDefined<XsSimpleTypeAttribute>());

            // XsCSharpListType
            if (csharpType.IsDefined<XsListTypeAttribute>())
                return new XsCSharpListType(schema, csharpType, declaringType);

            // XsUnionTypeAttribute
            if (csharpType.IsDefined<XsUnionTypeAttribute>())
                return new XsCSharpUnionType(schema, csharpType, declaringType);

            // XsCSharpEnumSimpleType
            var enumerationAttr = csharpType.GetAttribute(typeof(XsEnumerationAttribute));

            if (XsCSharpEnumSimpleType.GetEnumSimpleType(csharpType) != null)
                return new XsCSharpEnumSimpleType(
                    schema: schema,
                    csharpType: csharpType,
                    baseType: baseType,
                    declaringType: declaringType
                );

            // XsCSharpDerivedSimpleType
            return new XsCSharpConstrainedSimpleType(
                schema: schema,
                csharpType: csharpType,
                baseType: baseType,
                declaringType: declaringType
            );
        }

        private readonly XName m_xName;
        private readonly XsCSharpType m_declaringType;
        private readonly CSharpType m_alias;
        private readonly Lazy<CSharpType> m_deserializedType;
        private readonly Lazy<CSharpType> m_serializedType;
        private readonly Lazy<Func<object, object>> m_customSerialize;
        private readonly Lazy<Func<object, object>> m_customDeserialize;
        private readonly Lazy<Func<object, object>> m_customDenormalizeLiteral;

        protected XsCSharpSimpleType(
            XsCSharpSchema schema,
            CSharpType csharpType,
            XsCSharpType declaringType = null
        ) : base(schema, 
            csharpType) {

            // declaringType => annonymous simpleType
            m_declaringType = declaringType;
            if (m_declaringType == null)
                m_xName = XNamespace + Schema.ToXLocalName(Name);

            // serialized type
            m_serializedType = new Lazy<CSharpType>(() => 
                BaseType.DeserializedType ?? // inherit
                    CSharpLoader.GetType(typeof(string))  // XsAnySimpleType
            );

            var attr = csharpType.GetAttribute(typeof(XsAnyTypeAttribute));

            // alias
            m_alias = attr.AliasFor;

            m_deserializedType = new Lazy<CSharpType>(() =>
                attr.DeserializedType != null ?

                    // custom serialization to a different type
                    (CSharpType)attr.DeserializedType :

                    // no custom serialization
                    SerializedType
            );

            // custom serialize
            m_customSerialize = new Lazy<Func<object,object>>(() =>
                BindToMethod(
                    CSharpType.Load(),
                    typeof(XsSerializerAttribute),
                    DeserializedType.Load(),
                    SerializeMethodName
                )
            );

            // custom deserialize
            m_customDeserialize = new Lazy<Func<object, object>>(() =>
                BindToMethod(
                    CSharpType.Load(),
                    typeof(XsDeserializerAttribute),
                    SerializedType.Load(),
                    DeserializeMethodName
                )
            );

            // custom format literal
            m_customDenormalizeLiteral = new Lazy<Func<object, object>>(() =>
                BindToMethod(
                    CSharpType.Load(),
                    typeof(XsLiteralFormatterAttribute),
                    typeof(string),
                    DenormalizeLiteralMethodName
                )
            );
        }

        private Func<object, object> BindToMethod(
            Type clrType,
            Type attributeType,
            Type parameterType,
            string name) {

            // bind by attribute
            var decoratedMethods = clrType
                .Get<MethodInfo>()
                .Where(o => o.IsDefined(attributeType))
                .ToArray();

            // no custom serialization
            if (decoratedMethods.None())
                return null;

            // more than one custom serializer
            if (decoratedMethods.MoreThanOne())
                throw new XsLoaderException(
                    "More that one method on type '{0}' attributted with '{1}'."
                    .Substitute(clrType, attributeType.Name)
                );

            var decoratedMethod = decoratedMethods.Single();

            // bind by signature (value)
            var methodInfos = clrType.Get<MethodInfo>(
                name,
                new[] { parameterType },
                isStatic: true
            );

            // bind by signature (type, value)
            if (methodInfos.None())
                methodInfos = clrType.Get<MethodInfo>(
                    name,
                    new[] { typeof(XsCSharpType), parameterType },
                    isStatic: true
                );

            // failed to bind
            if (!methodInfos.Contains(decoratedMethod))
                throw new XsLoaderException(
                    ("Custom serialization method on type '{1}' attributed with '{0}' must be static, named '{2}', " +
                    "and have a single parameter of type '{3}' with an optional leading " +
                    "parameter of type 'XsCSharpSimpleType'."
                    ).Substitute(attributeType.Name, clrType, name, parameterType));

            // optimize binding
            return decoratedMethod.ToGenericDelegate(this);
        }

        internal abstract object CoerceFacetValue(object value, bool throwOnError = true);
        internal virtual dynamic CustomDeserialize(object value) {
            object result = value;

            if (IsXsdType)
                return result;

            var baseType = BaseType as XsCSharpSimpleType;
            if (baseType != null)
                result = baseType.CustomDeserialize(value);

            if (m_customDeserialize.Value != null)
                result = m_customDeserialize.Value(result);

            return result;
        }
        internal virtual object CustomSerialize(object value) {
            object result = value;

            if (IsXsdType)
                return result;

            if (m_customSerialize.Value != null)
                result = m_customSerialize.Value(result);

            var baseType = BaseType as XsCSharpSimpleType;
            if (baseType != null)
                result = baseType.CustomSerialize(result);

            return result;
        }
        internal virtual string CustomDenormalizeLiteral(string literal) {
            if (m_customDenormalizeLiteral.Value != null)
                literal = (string)m_customDenormalizeLiteral.Value(literal);

            return literal;
        }
        internal virtual CSharpType SerializedType {
            get { return m_serializedType.Value; }
        }

        public sealed override CSharpType Alias {
            get { return m_alias; }
        }
        public sealed override XsType XsType {
            get {
                if (XName == null)
                    return m_declaringType.XsType.SimpleContent;

                return XsSchema.GetType(XName); 
            }
        }

        public sealed override string Name {
            get { return CSharpType.Name; }
        }
        public sealed override XName XName {
            get { return m_xName; }
        }

        public sealed override bool IsStreamable {
            get { return XsType.IsStreamable; }
        }
        public override CSharpType DeserializedType {
            get { return m_deserializedType.Value; }
        }
        
        public sealed override dynamic Deserialize(string value) {

            // XsType deserialize
            var result = XsType.Deserialize(value);

            // XsCSharpType deserialize
            result = CustomDeserialize(result);

            return result;
        }
        public sealed override string Serialize(object value) {

            // XsCSharpType serialize
            var result = CustomSerialize(value);

            // XsType deserialize
            var literal = XsType.Serialize(result);

            return CustomDenormalizeLiteral(literal);
        }
    }
    internal class XsCSharpConstrainedSimpleType : XsCSharpSimpleType {

        private Lazy<XsCSharpType> m_baseType;

        internal XsCSharpConstrainedSimpleType(
            XsCSharpSchema schema,
            CSharpType csharpType,
            XsCSharpType baseType = null,
            XsCSharpType declaringType = null
        ) : base(
            schema: schema, 
            csharpType: csharpType,
            declaringType: declaringType) {

            m_baseType = new Lazy<XsCSharpType>(() => {

                if (baseType == null)
                    baseType = Loader.GetType(csharpType.BaseType);

                if (baseType == null && csharpType != typeof(Xs.AnyType))
                    throw new XsCSharpLoaderException(
                        "Type '{0}' base type failed to resolve to XsType."
                        .Substitute(csharpType)
                    );

                if (!baseType.IsSimpleType && csharpType != typeof(Xs.AnySimpleType))
                    throw new XsLoaderException(
                        "XsSimpleType '{0}' must derive from a SimpleType."
                        .Substitute(csharpType));

                return baseType;
            });

            // xsd metadata is hardcoded metadata so the attributed metadata must match it.
            if (IsXsdType) {
                ASSERT.Condition(DeserializedType == XsType.ClrType);
                ASSERT.Condition(Alias == null || Alias == XsType.Alias);
                ASSERT.Condition(BaseXsType == XsType.BaseType);
            }
        }

        internal sealed override XsType CreateXsType(XsTypeFactory xsTypeFactory) {
            return xsTypeFactory.CreateSimpleType(
                baseType: BaseXsType,
                facets: o => Facets(o),
                metadata: null
            );
        }
        internal override object CoerceFacetValue(object value, bool throwOnError = true) {

            if (!XsType.IsXsdType)
                return ((XsCSharpSimpleType)BaseType)
                    .CoerceFacetValue(value, throwOnError);

            // deserialize literal attribute values
            if (value is string)
                value = XsType.Deserialize((string)value, throwOnError);

            if (!XsType.ClrType.IsAssignableFrom(value.GetType())) {

                if (!throwOnError)
                    return null;

                throw new XsCSharpLoaderException(
                   ("Facet value '{0}' declared on '{1}' is of type '{2}' " + 
                    "which is not assignable to type '{3}'."
                   ).Substitute(value, CSharpType, value.GetType(), XsType.ClrType));
            }

            return value;
        }

        protected virtual IEnumerable<XsFacet> Facets(XsFacetFactory xsFacetFactory) {

            var facets = CSharpType.Attributes(typeof(XsFacetAttribute));
            foreach (var facet in facets) {

                var facetType = (Type)facet.GetAttributeType();

                // enumeration
                if (facetType == typeof(XsEnumerationAttribute)) {
                    yield return GetEnumerationFacet(xsFacetFactory, facet.Value);

                // pattern
                } else if (facetType == typeof(XsPatternAttribute)) {
                    yield return xsFacetFactory.CreatePatternFacet((string[])facet.Value);

                // all other facet types can be unified in a few steps
                } else {

                    #region General
                    // infer facet and property name from attribute name
                    var facetName = facetType.Name.ReplaceEnd("Attribute").ReplaceStart("Xs");

                    var valueBoundFacets = new[] {
                        typeof(XsMaximumExclusiveAttribute),
                        typeof(XsMaximumInclusiveAttribute),
                        typeof(XsMinimumExclusiveAttribute),
                        typeof(XsMinimumInclusiveAttribute),
                    };

                    var value = facet.Value;
                    if (valueBoundFacets.Contains(facetType)) {
                        if (value is long)
                            value = Convert.ChangeType(value, XsType.ClrType);
                        value = CoerceFacetValue(value);
                    }

                    if (value is CSharpSyntheticEnum)
                        value = value.Load();

                    var isFixed = (bool)facet.IsFixed;

                    // activate facet
                    var xsFacet = (XsFacet)typeof(XsFacetFactory).InvokeMember(
                        "Create" + facetName + "Facet",
                        BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public,
                        Type.DefaultBinder,
                        xsFacetFactory,
                        new object[] { value, isFixed }
                    );

                    yield return xsFacet;
                    #endregion
                }
            }
        }
        protected virtual XsFacet GetEnumerationFacet(XsFacetFactory xsFacetFactory, object value) {

            ASSERT.Condition(value is IEnumerable);

            // enumeration from list
            var values = (IEnumerable<object>)value;
            return xsFacetFactory.CreateEnumerationFacet(
                values.Select(o => CoerceFacetValue(o))
            );
        }

        public override XsCSharpType BaseType {
            get { return m_baseType.Value; }
        }
    }
    internal sealed class XsCSharpEnumSimpleType : XsCSharpConstrainedSimpleType {

        internal static CSharpType GetEnumSimpleType(CSharpType csharpType) {
            
            if (csharpType.IsEnum) {
                
                if (!csharpType.IsDefined<XsEnumTypeAttribute>())
                    return null;

                return csharpType;  
            }

            var attr = csharpType.GetAttribute(typeof(XsEnumerationAttribute));
            if (attr == null)
                return null;

            var type = attr.Value as CSharpType;
            if (type == null)
                return null;

            if (!type.IsEnum)
                return null;

            return type;
        }

        private readonly Lazy<Type> m_customSerialization;
        private readonly Lazy<XsCSharpType> m_baseType;
        private readonly CSharpType m_enumType;

        private readonly Lazy<bool> m_isDecimal;
        private readonly Lazy<Dictionary<CSharpEnumField, object>> m_enumToDeserializedValue;
        private readonly Lazy<Dictionary<object, CSharpEnumField>> m_deserializedValueToEnum;

        internal XsCSharpEnumSimpleType(
            XsCSharpSchema schema,
            CSharpType csharpType,
            XsCSharpType baseType,
            XsCSharpType declaringType = null
        ) : base(
            schema: schema, 
            csharpType: csharpType,
            baseType: baseType,
            declaringType: declaringType) {

            m_enumType = GetEnumSimpleType(csharpType);
            ASSERT.Condition(m_enumType != null);

            m_baseType = new Lazy<XsCSharpType>(() => {
                var loader = schema.Loader;

                // normal base type
                if (!CSharpType.IsEnum)
                    return base.BaseType;

                // baseType from XsEnumTypeAttribute
                var attr = csharpType.GetAttribute(typeof(XsEnumTypeAttribute));
                ASSERT.Condition(attr != null);
                baseType = loader.GetType((CSharpType)attr.BaseType);

                // string is default
                if (baseType == null)
                    baseType = loader.GetType(typeof(Xs.String));

                return baseType;
            });

            m_isDecimal = new Lazy<bool>(() => IsSubclassOf(XsLoader.Decimal));

            // deserialize
            m_enumToDeserializedValue =
                new Lazy<Dictionary<CSharpEnumField, object>>(() =>
                    GetDeserializedValues()
                );

            // serialize
            m_deserializedValueToEnum =
                new Lazy<Dictionary<object, CSharpEnumField>>(() =>
                    m_enumToDeserializedValue.Value.Reverse()
                );
        }

        private Dictionary<CSharpEnumField, object> GetDeserializedValues() {
            var deserializedType = DeserializedType.Load();

            var result = new Dictionary<CSharpEnumField, object>();
            foreach (var field in m_enumType.EnumFields()) {

                object value = null;

                // explicit value
                var attribute = field.LoadAttribute<XsDeserializedValueAttribute>();
                if (attribute != null)
                    value = attribute.Value;

                // implicit value
                else {

                    // value (enum)
                    if (deserializedType.IsEnum)
                        value = field.LoadValue();

                    // name (munged per Schema convention)
                    else if (deserializedType == typeof(string))
                        value = Schema.ToXLocalName(field.Name);

                    // raw value (int, long, etc)
                    else
                        value = field.ConstantValue;
                }

                // whatever the value, ensure it can be assigned to the deserialized type
                if (!deserializedType.IsAssignableFrom(value))
                    throw new XsLoaderException(
                       ("Enum '{0}' has a field '{1}' whose deserialized value '{2}' " +
                        "does not match the deserialize type '{3}'.").Substitute(
                            m_enumType, field.Name, value, deserializedType
                        ));

                result[field] = value;
            }

            return result;
        }

        protected override XsFacet GetEnumerationFacet(XsFacetFactory xsFacetFactory, object value) {

            var csharpType = value as CSharpType;
            ASSERT.Condition(csharpType != null);

            // XsEnumerationAttribute.Value must be an enum type
            if (!csharpType.IsEnum)
                throw new XsCSharpLoaderException(
                    "Type/Member '{0}' XsEnumeration contains a type '{1}' which is not an enum."
                    .Substitute(CSharpType, csharpType));

            // enum provides either ordinals or name
            var isDecimal = this.IsSubclassOf(XsLoader.Decimal);

            // get enumeration values from enum
            var enumFields = csharpType.EnumFields().ToArray();
            return xsFacetFactory.CreateEnumerationFacet(
                enumFields.Select(o =>
                    isDecimal ?
                        // decimal
                        o.ConstantValue :

                        // string
                        Schema.ToXLocalName(o.Name)
                ).ToArray()
            );
        }
        protected override IEnumerable<XsFacet> Facets(XsFacetFactory xsFacetFactory) {

            if (!CSharpType.IsDefined<XsEnumerationAttribute>())
                yield return GetEnumerationFacet(xsFacetFactory, CSharpType);

            foreach (var facet in base.Facets(xsFacetFactory))
                yield return facet;
        }

        internal override CSharpType SerializedType {
            get { return m_enumType; }
        }
        internal override object CustomSerialize(object deserializedValue) {

            // deserializedValue to enum
            var enumValue = base.CustomSerialize(deserializedValue);
            
            // enum to field
            var field = m_deserializedValueToEnum.Value
                .GetValueOrDefault(deserializedValue);

            object result = m_isDecimal.Value ?
                // field to ordinal
                field.ConstantValue :

                // field to name
                Schema.ToXLocalName(field.Name);

            return result;
        }
        internal override dynamic CustomDeserialize(object serializedValue) {

            var field = m_isDecimal.Value ?
                
                // field by ordinal
                m_enumType.GetEnumValue(serializedValue) :

                // field by name
                m_enumType.GetEnumField(
                    Schema.ToCSharpName((string)serializedValue)
                );

            // field to enum
            var enumValue = m_enumToDeserializedValue.Value[field];

            // enum to deserializedValue
            var result = base.CustomDeserialize(enumValue);

            return result;
        }

        public override XsCSharpType BaseType {
            get { return m_baseType.Value; }
        }
    }
    internal sealed class XsCSharpUnionType : XsCSharpSimpleType {
        
        private readonly XsCSharpType[] m_unionTypes;
        private readonly Lazy<Dictionary<Type, XsCSharpSimpleType>> m_typeFromClrType;

        internal XsCSharpUnionType(
            XsCSharpSchema schema,
            CSharpType csharpType,
            XsCSharpType declaringType = null
        ) : base(
            schema: schema, 
            csharpType: csharpType,
            declaringType: declaringType) {

            if (csharpType.IsDefined<XsFacetAttribute>())
                throw new XsCSharpLoaderException(
                    "Type '{0}' is a union and so cannot have any facets."
                    .Substitute(csharpType));

            if (csharpType.BaseType != typeof(object))
                throw new XsCSharpLoaderException(
                    "Type '{0}' is a union and so must derive from object."
                    .Substitute(csharpType));

            // load union types
            var attr = csharpType.GetAttribute(typeof(XsUnionTypeAttribute));
            m_unionTypes = ((CSharpType[])attr.Types).Select(o => {
                var unionType = Loader.GetType(o);
                if (unionType == null || !unionType.IsSimpleType)
                    throw new XsCSharpLoaderException(
                        "Union '{0}' declared member '{1}' which did not resolve to a simple XsCSharpType."
                        .Substitute(csharpType, o));
                return unionType;
            }).ToArray();

            m_typeFromClrType = new Lazy<Dictionary<Type, XsCSharpSimpleType>>(() => {

                // ensure unique deserialized types
                var duplicates =
                    from o in UnionTypes()
                    group o by o.DeserializedType.Load() into o
                    where o.MoreThanOne()
                    select new { o.Key, Types = o };

                var duplicate = duplicates.FirstOrDefault();
                if (duplicate != null)
                    throw new XsCSharpLoaderException(
                        "Union '{0}' is composed of type '{1}' and '{2}' that both deserialize to '{3}'."
                        .Substitute(this,
                            duplicate.Types.First(),
                            duplicate.Types.Second(),
                            duplicate.Key)
                    );

                // XsCSharpType from ClrType
                var typeFromClrType = UnionTypes()
                    .Cast<XsCSharpSimpleType>()
                    .ToDictionary(o => o.DeserializedType.Load());

                // ensure unique deserialized types (via IsAssignableFrom)
                var assignableFrom =
                    from lhs in typeFromClrType.Keys
                    from rhs in typeFromClrType.Keys
                    where lhs != rhs && lhs.IsAssignableFrom(rhs)
                    select new { lhs, rhs };

                if (assignableFrom.Any())
                    throw new XsCSharpLoaderException(
                        "Union '{0}' is composed of type '{1}' that is assignable from '{2}'."
                        .Substitute(this, assignableFrom.First().lhs, assignableFrom.First().rhs));

                return typeFromClrType;                    
            });
        }

        internal override object CoerceFacetValue(object value, bool throwOnError = true) {

            foreach (XsCSharpSimpleType type in UnionTypes()) {
                var result = type.CoerceFacetValue(value, false);
                if (result != null)
                    return result;
            }

            if (throwOnError)
                throw new XsCSharpLoaderException(
                    "Cannot coerce facet value '{0}' on type '{1}'."
                    .Substitute(value, CSharpType));

            return null;
        }
        internal override CSharpType SerializedType {
            get { return CSharpLoader.GetType(typeof(object)); }
        }
        internal override XsType CreateXsType(XsTypeFactory xsTypeFactory) {
            return xsTypeFactory.CreateUnionType(
                unionTypes: m_unionTypes.Select(o => o.XsType).ToArray(),
                metadata: null
            );
        }
        internal override object CustomSerialize(object value) {
            if (value == null)
                return null;

            // apply user custom serializer
            value = base.CustomSerialize(value);

            // get type
            var type = m_typeFromClrType.Value.GetValueOrDefault(value.GetType());

            // use type to preform custom serialization
            var result = type.CustomSerialize(value);
            return result;
        }
        internal override dynamic CustomDeserialize(object value) {
            ASSERT.Condition(value is XsTypedValue);

            // unpackage typed result
            var xsTypedValue = (XsTypedValue)value;
            value = xsTypedValue.Value;

            // get type
            var type = (XsCSharpSimpleType)Loader.GetType(xsTypedValue.Type);
            ASSERT.Condition(type != null);

            // use typed result type to deserialize value
            value = type.CustomDeserialize(value);

            // apply user custom serializer
            value = base.CustomDeserialize(value);

            // package in XsCSharpTypedValue
            return value;
        }

        public override IEnumerable<XsCSharpType> UnionTypes(bool declaredOnly = true) {
            if (declaredOnly)
                return m_unionTypes;

            return m_unionTypes.Where(o => !o.IsUnion).Concat(
                m_unionTypes.SelectMany(o => o.UnionTypes())
            );
        }
        public override XsCSharpType BaseType {
            get { return Loader.GetType(typeof(Xs.AnySimpleType)); }
        }
    }
    internal sealed class XsCSharpListType : XsCSharpSimpleType {
        private readonly XsCSharpSimpleType m_listType;

        internal XsCSharpListType(
            XsCSharpSchema schema,
            CSharpType csharpType,
            XsCSharpType declaringType = null
        ): base(
            schema: schema,
            csharpType: csharpType,
            declaringType: declaringType) {

            if (csharpType.IsDefined<XsFacetAttribute>())
                throw new XsCSharpLoaderException(
                    "Type '{0}' is a list and so cannot have any facets."
                    .Substitute(csharpType));

            if (csharpType.BaseType != typeof(object))
                throw new XsCSharpLoaderException(
                    "Type '{0}' is a list and so must derive from object."
                    .Substitute(csharpType));

            // load list item type
            var attr = csharpType.GetAttribute(typeof(XsListTypeAttribute));
            m_listType = Loader.GetType((CSharpType)attr.ItemType) as XsCSharpSimpleType;

            if (m_listType == null)
                throw new XsLoaderException(
                    "List type '{0}' item type '{1}' did not resolve to a simple XsCSharpType."
                    .Substitute(this, (object)attr.ItemType));
        }

        internal override object CoerceFacetValue(object value, bool throwOnError = false) {
 
            // deserialize literal attribute values
            if (value is string)
                value = XsType.Deserialize((string)value, throwOnError);

            if (!XsType.ClrType.IsAssignableFrom(value.GetType())) {

                if (!throwOnError)
                    return null;

                throw new XsCSharpLoaderException(
                   ("Facet value '{0}' declared on '{1}' is of type '{2}' " +
                    "which is not assignable to type '{3}'."
                   ).Substitute(value, CSharpType, value.GetType(), XsType.ClrType));
            }

            return value;
        }
        internal override CSharpType SerializedType {
            get { 
                return CSharpLoader.GetType(typeof(IEnumerable<>))
                    .MakeGenericType(ListType.DeserializedType); 
            }
        }
        internal override XsType CreateXsType(XsTypeFactory xsTypeFactory) {
            return xsTypeFactory.CreateListType(
                listType: m_listType.XsType,
                metadata: null
            );
        }

        public override XsCSharpType ListType {
            get { return m_listType; }
        }
        public override XsCSharpType BaseType {
            get { return Loader.GetType(typeof(Xs.AnySimpleType)); }
        }
    }

    public sealed class XsCSharpAttribute {
    }
    public sealed class XsCSharpAttributeGroup {
        private XsCSharpSchema m_schema;

        internal XsCSharpAttributeGroup(XsCSharpSchema schema) {
            m_schema = schema;
        }

        public XsCSharpSchema Schema {
            get { return m_schema; }
        }
        public string Name {
            get { return null; }
        }
        public XName XName {
            get { return null; }
        }
    }

    public abstract class XsCSharpComplexContent {
        internal static XsCSharpSchema GetDeclaringSchema(object parent) {
            var schema = parent as XsCSharpSchema;
            if (schema == null) {

                var type = GetDeclaringType(parent);
                if (type == null)
                    return null;

                schema = type.Schema;
            }

            return schema;
        }
        internal static XsCSharpType GetDeclaringType(object parent) {
            var type = parent as XsCSharpType;
            if (type == null) {

                var content = parent as XsCSharpComplexContent;
                if (content == null)
                    return null;

                type = content.DeclaringType;
            }

            return type;
        }

        private readonly XsCSharpSchema m_schema;
        private readonly Lazy<XsCSharpComplexContent> m_declaringContent;
        private readonly Lazy<XsCSharpType> m_declaringType;
        private readonly Lazy<XsComplexContent> m_xsContent;
        private readonly Lazy<IEnumerable<XsCSharpComplexContent>> m_content;

        internal XsCSharpComplexContent(XsCSharpSchema schema, XsComplexContent xsContent = null)
            : this(schema, new Lazy<XsComplexContent>(() => xsContent)) {
        }
        internal XsCSharpComplexContent(XsCSharpSchema schema, Lazy<XsComplexContent> xsContent = null) {
            m_schema = schema;
            m_xsContent = xsContent;
            m_declaringType = new Lazy<XsCSharpType>(() => Loader.GetType(XsContent.DeclaringType));
            m_declaringContent = new Lazy<XsCSharpComplexContent>(() => Schema.GetContent(XsContent.DeclaringContent));

            m_content = new Lazy<IEnumerable<XsCSharpComplexContent>>(() => {
                return XsContent.Content().Select(o => {
                    XsCSharpComplexContent result;

                    if (o is XsSequence)
                        result = new XsCSharpSequence(Schema, o);
                    else if (o is XsChoice)
                        result = new XsCSharpChoice(Schema, o);
                    else if (o is XsElement)
                        result = new XsCSharpElement(Schema, o);
                    else if (o is XsAll)
                        result = new XsCSharpAll(Schema, o);
                    else if (o is XsAny)
                        result = new XsCSharpAny(Schema, o);
                    else 
                        throw new InvalidOperationException();

                    return result;
                }).ToArray();
            });
        }

        public XsComplexContent XsContent {
            get { return m_xsContent.Value; }
        }
        public XsCSharpLoader Loader {
            get { return Schema.Loader; }
        }
        public XsCSharpSchema Schema {
            get { return m_schema; }
        }
        public XsCSharpComplexContent DeclaringContent {
            get { return m_declaringContent.Value; }
        }
        public XsCSharpType DeclaringType {
            get { return m_declaringType.Value; }
        }
        public int MinOccurs {
            get { return XsContent.MinOccurs; }
        }
        public int MaxOccurs {
            get { return XsContent.MaxOccurs; }
        }
        public IEnumerable<XsCSharpComplexContent> Content() {
            return m_content.Value;
        }
    }
    public sealed class XsCSharpElement : XsCSharpComplexContent {
        private readonly string m_name;
        private readonly XsCSharpType m_type;

        internal XsCSharpElement(XsCSharpSchema schema, XsComplexContent xsContent)
            : base(schema, xsContent) {

            m_name = XsElement.Name;
            m_type = Loader.GetType(XsElement.Type);
        }
        internal XsCSharpElement(XsCSharpSchema schema, string name, XsCSharpType type)
            : base(schema, new Lazy<XsComplexContent>(() => schema.XsSchema.GetRootElement(name))) {

            m_name = name;
            m_type = type;
        }

        public string Name {
            get { return m_name; }
        }
        public XName XName {
            get { return XsElement.XName; }
        }
        public XsElement XsElement {
            get { return (XsElement)XsContent; }
        }
        public XsForm Form {
            get { return XsElement.Form; }
        }
        public object Default {
            get { return XsElement.Default; }
        }
        public XsCSharpType Type {
            get { return m_type; }
        }
    }
    public sealed class XsCSharpSequence : XsCSharpComplexContent {

        internal XsCSharpSequence(XsCSharpSchema schema, XsComplexContent xsContent)
            : base(schema, xsContent) {
        }

        public XsSequence XsSequence {
            get { return (XsSequence)XsContent; }
        }
    }
    public sealed class XsCSharpChoice : XsCSharpComplexContent {

        internal XsCSharpChoice(XsCSharpSchema schema, XsComplexContent xsContent)
            : base(schema, xsContent) {
        }

        public XsChoice XsChoice {
            get { return (XsChoice)XsContent; }
        }
    }
    public sealed class XsCSharpAll : XsCSharpComplexContent {

        internal XsCSharpAll(XsCSharpSchema schema, XsComplexContent xsContent)
            : base(schema, xsContent) {
        }

        public XsAll XsAll {
            get { return (XsAll)XsContent; }
        }
    }
    public sealed class XsCSharpAny : XsCSharpComplexContent {

        internal XsCSharpAny(XsCSharpSchema schema, XsComplexContent xsContent)
            : base(schema, xsContent) {
        }

        public XsAny XsAny {
            get { return (XsAny)XsContent; }
        }
        public XsProcessContents ProcessContents {
            get { return XsAny.ProcessContents; }
        }
        public IEnumerable<XNamespace> Namespaces() {
            return XsAny.Namespaces();
        }
        public XsNamespaceType Type {
            get { return XsAny.Type; }
        }
    }

    public sealed class XsCSharpMetadata {
    }
}
namespace King.Xml.CSharp.Reflection.Emit {

    public static class XsCSharpResolver {

        public static string ResolveName(
            object key,
            XNamespace xNamespace,
            Func<string, string> toCSharpName,
            XsForm xsForm = XsForm.Qualified,
            Func<object, XName> resolveXName = null) {

            if (xsForm == XsForm.Unqualified)
                xNamespace = XNamespace.None;

            return XsResolver.ResolveName(key, xNamespace, toCSharpName, resolveXName);
        }

        public static CSharpType ResolveNestedType(
            object csharpTypeOrNameOrXName,
            CSharpType declaringType,
            XNamespace expectedNamespace,
            Func<string, string> toName) {

            var o = csharpTypeOrNameOrXName;

            // csharpType
            var csharpType = o as CSharpType;
            if (csharpType == null) {

                var name = ResolveName(o, expectedNamespace, toName);

                csharpType = declaringType.GetNestedType(name);
                if (csharpType == null)
                    return null;
            }

            return csharpType;
        }
    }
    public sealed class XsCSharpSchemaFactory {
        private XsCSharpLoader m_loader;
        private CSharpType m_csharpType;

        internal XsCSharpSchemaFactory(
            XsCSharpLoader loader, 
            CSharpType csharpType) {

            m_loader = loader;
            m_csharpType = csharpType;
        }

        public XsCSharpLoader Loader {
            get { return m_loader; }
        }
        public CSharpType CSharpType {
            get { return m_csharpType; }
        }

        public XsCSharpSchema CreateSchema(
            Func<XsCSharpSchema, IEnumerable<object>> complexTypeNames = null,
            Func<XsCSharpTypeFactory, XsCSharpType> complexTypeFactory = null,

            Func<XsCSharpSchema, IEnumerable<object>> attributeGroupNames = null,
            Func<XsCSharpAttributeGroupFactory, XsCSharpAttributeGroup> attributeGroupFactory = null,

            Func<XsCSharpSchema, IEnumerable<object>> rootElementNames = null,
            Func<XsCSharpRootElementFactory, XsCSharpElement> rootElementFactory = null
        ) {
            if (complexTypeNames == null)
                complexTypeNames = o => Enumerable.Empty<object>();

            if (complexTypeFactory == null)
                complexTypeFactory = o => null;

            if (attributeGroupNames == null)
                attributeGroupNames = o => Enumerable.Empty<object>();

            if (attributeGroupFactory == null)
                attributeGroupFactory = o => null;

            if (rootElementNames == null)
                rootElementNames = o => Enumerable.Empty<object>();

            if (rootElementFactory == null)
                rootElementFactory = o => null;

            return new XsCSharpSchema(
                loader: m_loader,
                schemaType: m_csharpType,

                complexTypeNames: complexTypeNames,
                complexTypeFactory: complexTypeFactory,

                attributeGroupNames: attributeGroupNames,
                attributeGroupFactory: attributeGroupFactory,

                rootElementNames: rootElementNames,
                rootElementFactory: rootElementFactory
            );
        }
    }
    public sealed class XsCSharpSimpleContentFactory {
        private XsCSharpSchema m_schema;
        private XsCSharpType m_complexType;

        internal XsCSharpSimpleContentFactory(
            XsCSharpType complexType) {

            m_complexType = complexType;
        }

        public XsCSharpLoader Loader {
            get { return Schema.Loader; }
        }
        public XsCSharpSchema Schema {
            get { return ComplexType.Schema; }
        }
        public XsCSharpType ComplexType {
            get { return m_complexType; }
        }

        public XsCSharpType CreateSimpleContent(
            CSharpType restriction,
            XsCSharpType declaringType,
            XsCSharpType baseType) {

            ASSERT.Condition(ComplexType.BaseType.SimpleContent == null ||
                ComplexType.BaseType.SimpleContent == baseType);

            // if no new restrictions simply reuse baseType
            if (!restriction.IsDefined(typeof(XsAnyTypeAttribute)))
                return baseType;

            // new restrictions so create a new anonymous simple type
            return XsCSharpSimpleType.Activate(
                schema: Schema,
                csharpType: restriction,
                baseType: baseType,
                declaringType: declaringType
            );
        }    
    }
    public sealed class XsCSharpTypeFactory {
        private XsCSharpSchema m_schema;
        private CSharpType m_csharpType;

        internal XsCSharpTypeFactory(XsCSharpSchema schema, CSharpType csharpType) {
            m_schema = schema;
            m_csharpType = csharpType;
        }

        public XsCSharpLoader Loader {
            get { return m_schema.Loader; }
        }
        public XsCSharpSchema Schema {
            get { return m_schema; }
        }
        public CSharpType CSharpType {
            get { return m_csharpType; }
        }

        public XsCSharpType CreateComplexTypeWithSimpleContent(
            XsCSharpType baseType,
            Func<XsCSharpSimpleContentFactory, XsCSharpType> simpleContent,
            CSharpType alias = null,
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory = null,
            bool isAbstract = false,
            XsFinal final = XsFinal.None,
            XsMetadataFactory metadata = null
        ) {
            return new XsCSharpComplexType(
                schema: Schema,
                baseType: baseType,
                csharpType: CSharpType,
                csharpTypeAlias: alias,
                getSimpleContent: simpleContent,
                getXsType: (complexType, xsTypeFactory) => 
                    xsTypeFactory.CreateComplexTypeWithSimpleContent(
                        baseType: baseType.XsType,
                        simpleContent: complexType.CreateXsSimpleContent,
                        isAbstract: isAbstract,
                        final: final,
                        attributeFactory: attributeFactory,
                        metadata: metadata
                    )
            );
        }

        public XsCSharpType CreateComplexTypeWithComplexContent(
            XsCSharpType baseType,
            CSharpType alias = null,
            Func<XsComplexContentFactory, XsComplexContent> contentFactory = null,
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory = null,
            bool isRestriction = false,
            bool isAbstract = false,
            bool isMixed = false,
            XsFinal final = XsFinal.None,
            XsMetadataFactory metadata = null
        ) {
            return new XsCSharpComplexType(
                schema: Schema,
                baseType: baseType,
                csharpType: CSharpType,
                csharpTypeAlias: alias,
                getSimpleContent: null,
                getXsType: (t, o) => o.CreateComplexTypeWithComplexContent(
                    baseType: baseType.XsType,
                    isRestriction: isRestriction,
                    isAbstract: isAbstract,
                    isMixed: isMixed,
                    final: final,
                    contentFactory: contentFactory,
                    attributeFactory: attributeFactory,
                    metadata: metadata
                )
            );
        }
    }
    public sealed class XsCSharpAttributeGroupFactory {
        private XsCSharpSchema m_schema;
        private XName m_xName;

        internal XsCSharpAttributeGroupFactory(
            XsCSharpSchema schema,
            XName xName) {
            m_schema = schema;
            m_xName = xName;
        }

        public XsCSharpLoader Loader {
            get { return m_schema.Loader; }
        }
        public XsCSharpSchema Schema {
            get { return m_schema; }
        }
        public XName XName {
            get { return m_xName; }
        }

        public XsCSharpAttributeGroup CreateAttributeGroup(
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory,
            XsMetadataFactory metadata = null
        ) {

            throw new NotImplementedException();
            //return new XsCSharpAttributeGroup(
            //    schema: Schema,
            //    xName: XName,
            //    attributeFactory: attributeFactory,
            //    metadata: metadata
            //);
        }
    }
    public sealed class XsCSharpAttributeFactory {
        private XsSchema m_schema;
        private XsType m_declaringType;
        private XsAttributeGroup m_declaringAttributeGroup;

        internal XsCSharpAttributeFactory(XsSchema schema, XsType declaringType) {
            m_schema = schema;
            m_declaringType = declaringType;
        }
        internal XsCSharpAttributeFactory(XsSchema schema, XsAttributeGroup declaringAttributeGroup) {
            m_schema = schema;
            m_declaringAttributeGroup = declaringAttributeGroup;
        }

        public XsLoader Loader {
            get { return Schema.Loader; }
        }
        public XsSchema Schema {
            get { return m_schema; }
        }
        public XsType DeclaringType {
            get { return m_declaringType; }
        }
        public XsAttributeGroup DeclaringAttributeGroup {
            get { return m_declaringAttributeGroup; }
        }

        public XsAttribute CreateAttribute(
            string name,
            XsType xsType,
            XsForm form = XsForm.Undefined,
            XsAttributeUse use = XsAttributeUse.Required,
            object @default = null,
            XsMetadataFactory metadata = null
        ) {

            return new XsAttribute(
                schema: Schema,
                declaringType: DeclaringType,
                declaringAttributeGroup: DeclaringAttributeGroup,

                name: name,
                form: form,
                type: xsType,
                use: use,
                @default: @default,

                metadata: metadata
            );
        }

        public XsAttribute CreateAnyAttribute(
            XsProcessContents processContents = XsProcessContents.Strict,
            IEnumerable<XNamespace> namespaces = null,
            XsNamespaceType namespaceType = XsNamespaceType.None,
            XsMetadataFactory metadata = null
        ) {

            return new XsAttribute(
                schema: Schema,
                declaringType: DeclaringType,
                declaringAttributeGroup: DeclaringAttributeGroup,

                processContents: processContents,
                namespaces: namespaces,
                namespaceType: namespaceType,

                metadata: metadata
            );
        }

        public XsAttributeGroup CreateAttributeGroup() {
            throw new NotImplementedException();
        }
    }
    public sealed class XsCSharpRootElementFactory {
        private readonly XsCSharpSchema m_schema;
        private readonly string m_name;

        internal XsCSharpRootElementFactory(
            XsCSharpSchema schema,
            string name) {

            m_schema = schema;
            m_name = name;
        }

        public XsCSharpLoader Loader {
            get { return Schema.Loader; }
        }
        public XsCSharpSchema Schema {
            get { return m_schema; }
        }
        public string Name {
            get { return m_name; }
        }

        public XsCSharpElement CreateRootElement(
            XsCSharpType type,
            XsMetadataFactory metadata = null
        ) {
            return new XsCSharpElement(Schema, Name, type);
        }
    }
}
